THIS_DIR := $(dir $(lastword $(MAKEFILE_LIST)))

# Relative path to Graphene root
GRAPHENEDIR ?= $(THIS_DIR)../..
SGX_SIGNER_KEY ?= $(GRAPHENEDIR)/Pal/src/host/Linux-SGX/signer/enclave-key.pem
BZIP2_URL ?= https://people.csail.mit.edu/smcc/projects/single-file-programs/bzip2.c
BZIP2_HASH ?= 89f331ce93cbf0ee7318802f440f1d7594bb78cf1a82069f2288e0459ec8d729
GZIP_URL ?= https://people.csail.mit.edu/smcc/projects/single-file-programs/gzip.c
GZIP_HASH ?= 7ec7d87822e6497648580064756f64e47dbd085727910ebbc52a7c240a88dc27

ifeq ($(DEBUG),1)
GRAPHENEDEBUG = inline
else
GRAPHENEDEBUG = none
endif

include ../../Scripts/Makefile.configs

# All the tools/executables required for this example (alphabetic order).
TOOLS = as cc1 collect2 gcc ld

# awk '{print $NF}' ...  print last field.
BINUTILS_VERSION ?= $(shell ld -v | awk '{print $$NF}')
GCC_MAJOR_VERSION ?= $(shell gcc -v 2>&1 | tail -n1 | awk '{print $$3}' | awk 'BEGIN {FS="."}{print $$1}')
GCC_LIB_PATH ?= /usr/lib/gcc/$(ARCH_LONG)

.PHONY: all
ifeq ($(SGX),1)
all: $(addsuffix .token, $(TOOLS)) pal_loader test_files/bzip2.c test_files/gzip.c
else
all: $(addsuffix .manifest, $(TOOLS)) pal_loader test_files/bzip2.c test_files/gzip.c
endif

%.manifest: %.manifest.template
	sed -e 's|$$(GRAPHENEDIR)|'"$(GRAPHENEDIR)"'|g' \
		-e 's|$$(GRAPHENEDEBUG)|'"$(GRAPHENEDEBUG)"'|g' \
		-e 's|$$(BINUTILS_VERSION)|'"$(BINUTILS_VERSION)"'|g' \
		-e 's|$$(GCC_MAJOR_VERSION)|'"$(GCC_MAJOR_VERSION)"'|g' \
		-e 's|$$(GCC_LIB_PATH)|'"$(GCC_LIB_PATH)"'|g' \
		-e 's|$$(CC1_TRUSTED_LIBS)|'"`cat cc1-trusted-libs`"'|g' \
		-e 's|$$(LD_TRUSTED_FILES)|'"`cat ld-gcc-$(GCC_MAJOR_VERSION)-trusted-files`"'|g' \
		-e 's|$$(ARCH_LIBDIR)|'"$(ARCH_LIBDIR)"'|g' \
		$< > $@

# Prevent .manifest/.manifest.sgx from automatically being deleted by make
.SECONDARY: $(addsuffix .manifest.sgx, $(TOOLS))
.SECONDARY: $(addsuffix .manifest, $(TOOLS))
.SECONDARY: gcc.sig

# Rules to generate the SGX-specific manifest (.manifest.sgx), the enclave signature (.sig), and the
# enclave initialization token (.token).
%.manifest.sgx: %.manifest
	$(GRAPHENEDIR)/Pal/src/host/Linux-SGX/signer/pal-sgx-sign \
		-libpal $(GRAPHENEDIR)/Runtime/libpal-Linux-SGX.so \
		-key $(SGX_SIGNER_KEY) \
		-manifest $< -output $@

# .sig file is actually generated by the above target %.manifest.sgx. The %.sig target is required
# make `make` aware of this. Also, the target must actually have at least one command, otherwise it
# does not work as intended.
%.sig: %.manifest.sgx
	@echo -n ""

%.token: %.sig
	$(GRAPHENEDIR)/Pal/src/host/Linux-SGX/signer/pal-sgx-get-token -output $@ -sig $<

# Create link to pal_loader for convenience.
pal_loader:
	ln -s $(GRAPHENEDIR)/Runtime/pal_loader $@

# Dependencies between the various tools.
gcc.manifest.sgx: as.sig cc1.sig collect2.sig
collect2.manifest.sgx: ld.sig
cc1.manifest: cc1-trusted-libs

test_files/bzip2.c:
	$(GRAPHENEDIR)/Scripts/download --output $@ --sha256 $(BZIP2_HASH) --url $(BZIP2_URL)

# the file hosted by the authors doesn't compile...
test_files/gzip_broken.c:
	$(GRAPHENEDIR)/Scripts/download --output $@ --sha256 $(GZIP_HASH) --url $(GZIP_URL)

test_files/gzip.c: test_files/gzip_broken.c test_files/gzip.patch
	patch test_files/gzip_broken.c -i test_files/gzip.patch -o $@

# We need to replace Glibc dependencies with Graphene-specific Glibc. The Glibc
# binaries are already listed in the manifest template, so we can skip them
# from the ldd results
GLIBC_DEPS = linux-vdso /lib64/ld-linux-x86-64 libc libm librt libdl libutil libpthread

# List all the cc1 dependencies, besides Glibc libraries
.INTERMEDIATE: cc1-ldd
cc1-ldd: $(GCC_LIB_PATH)/$(GCC_MAJOR_VERSION)/cc1
%-ldd:
	@for F in $^; do \
		ldd $$F >> $@ || exit 1; done

%-deps: %-ldd
	cat $< | awk '{if ($$2 =="=>") {split($$1,s,/\./); print s[1]}}' \
		| sort | uniq | grep -v -x $(patsubst %,-e %,$(GLIBC_DEPS)) > $@

# Generate manifest rules for trusted shared libraries
%-trusted-libs: %-deps
	for F in `cat $<`; do \
		P=`cat $*-ldd | grep $$F | awk '{print $$3; exit}'`; \
		N=`echo $$F | tr --delete '-'`; \
		echo -n "sgx.trusted_files.$$N = file:$$P\\\\n"; \
	done > $@

.PHONY: check
check: all
	@echo "\n\nCompiling hello.c..."
	./pal_loader gcc.manifest test_files/helloworld.c -o test_files/hello
	@chmod 755 test_files/hello
	-./test_files/hello
	$(RM) test_files/hello

	@echo "\n\nCompiling bzip2.c..."
	./pal_loader ./gcc.manifest test_files/bzip2.c -o test_files/bzip2
	@chmod 755 test_files/bzip2
	$(RM) bzip2.tmp
	@cp -f test_files/bzip2 test_files/bzip2.copy
	./test_files/bzip2 -z test_files/bzip2.copy
	./test_files/bzip2 -d test_files/bzip2.copy.bz2
	diff -q test_files/bzip2 test_files/bzip2.copy
	$(RM) test_files/bzip2 test_file/bzip2.copy

	@echo "\n\nCompiling gzip.c..."
	./pal_loader ./gcc.manifest test_files/gzip.c -o test_files/gzip
	@chmod 755 test_files/gzip
	@cp -f test_files/gzip test_files/gzip.copy
	./test_files/gzip test_files/gzip.copy
	./test_files/gzip -d test_files/gzip.copy.gz
	diff -q test_files/gzip test_files/gzip.copy
	$(RM) test_files/gzip test_files/gzip.copy

.PHONY: clean
clean:
	$(RM) *.manifest *.manifest.sgx *.sig *.token pal_loader
	$(RM) *-deps *-ldd *-trusted-libs
	$(RM) test_files/gzip.copy test_files/bzip2.copy

.PHONY: distclean
distclean: clean
	$(RM) test_files/gzip_broken.c test_files/bzip2.c test_files/gzip.c
